/*
 * Project CelestialBeing: consteval.hpp
 * Created: 2023/12/18
 * This program uses MulanPSL2. See LICENSE for more.
 */

#ifndef CELESTIALBEING_CONSTEVAL_HPP
#define CELESTIALBEING_CONSTEVAL_HPP

#include "cb/common/celestial_being_base.hpp"
#include "cb/common/celestial_being_constants.hpp"

CB_NAMESPACE_BEGIN


// Retrieve the string length of a positive digit in dec.
template<uint32_t Number>
struct TGetDecDigitStrLength {
    static constexpr uint32_t value = 1 + TGetDecDigitStrLength<Number / 10>::_internal_value;
    static constexpr uint32_t _internal_value = 1 + TGetDecDigitStrLength<Number / 10>::_internal_value;
};
template<>
struct TGetDecDigitStrLength<0> {
    static constexpr uint32_t value = 1;
    static constexpr uint32_t _internal_value = 0;
};

template<size_t InCharArrayLen1, size_t InCharArrayLen2>
consteval std::array<char, InCharArrayLen1 + InCharArrayLen2> ConstConcatCharArray (const std::array<char, InCharArrayLen1> &Arr1, const std::array<char, InCharArrayLen2> &Arr2) {
    std::array<char, InCharArrayLen1 + InCharArrayLen2> result {};
    std::copy(Arr1.begin(), Arr1.end(), result.begin());
    std::copy(Arr2.begin(), Arr2.end(), result.begin() + InCharArrayLen1);
    return result;
}

template<size_t InCharArrayLen1, const char * >
consteval std::array<char, InCharArrayLen1 + 1> ConstConcatCharArray (const std::array<char, InCharArrayLen1> &Arr1, char Char) {
    std::array<char, InCharArrayLen1 + 1> result;
    std::copy(Arr1.begin(), Arr1.end(), result.begin());
    result[InCharArrayLen1] = Char;
    return result;
}

template<uint32_t Number>
struct TCharArray {
    static constexpr uint32_t Length = TGetDecDigitStrLength<Number>::value;
    static constexpr std::array<char, Length> value = TConcatCharArray(TCharArray<Number / 10>::value, '0' + Number % 10);
};

template<typename TFirst, typename...T>
inline consteval uint64_t ConstHashNumberSeries(TFirst Arg, T...Args) {
    if constexpr (sizeof...(Args) == 0) return Arg;
    else {
        uint64_t remains = ConstHashNumberSeries(Args...);
        uint64_t hash = (remains + Arg) + ((remains + Arg) << 10);
        hash = hash ^ (hash >> 6);
        return hash;
    }
}

template<typename TFirst, typename...T>
inline uint64_t HashNumberSeries(TFirst Arg, T...Args) {
    if constexpr (sizeof...(Args) == 0) return Arg;
    else {
        uint64_t remains = HashNumberSeries(Args...);
        uint64_t hash = (remains + Arg) + ((remains + Arg) << 10);
        hash = hash ^ (hash >> 6);
        return hash;
    }
}

template<size_t InCharArrayLen>
consteval size_t ConstFindNextPlaceHolderPosition (const std::array<char, InCharArrayLen> &Arr, size_t StartPos = 0) {
    for (size_t i = StartPos; i < InCharArrayLen-1; ++i) {
        if (Arr[i] == '{' && Arr[i+1] == '}') return i;
    }
    return InCharArrayLen;
}

template<size_t InCharArrayLen, size_t StartPos, size_t EndPos>
consteval auto ConstSubStr (const std::array<char, InCharArrayLen> &Arr) {
    std::array<char, EndPos - StartPos> result {};
    std::copy(Arr.begin() + StartPos, Arr.begin() + EndPos, result.begin());
    return result;
}

template<size_t InCharArrayLen, size_t StartPos>
consteval auto ConstSubStr (const std::array<char, InCharArrayLen> &Arr) {
    std::array<char, InCharArrayLen - StartPos> result {};
    std::copy(Arr.begin() + StartPos, Arr.end(), result.begin());
    return result;
}

template<size_t InCharArrayLen1, size_t InCharArrayLen2>
consteval auto ConstReplacePlaceHolderAtPosition (const std::array<char, InCharArrayLen1> &Arr, size_t Pos, const std::array<char, InCharArrayLen2> & Str) {
    std::array<char, InCharArrayLen1 + InCharArrayLen2 - 2> result {};
    std::copy(Arr.begin(), Arr.begin() + Pos, result.begin());
    std::copy(Str, Str + InCharArrayLen2, result.begin() + Pos);
    std::copy(Arr.begin() + Pos + 2, Arr.end(), result.begin() + Pos + InCharArrayLen2);
    return result;
}

template<size_t InFormatCharArrayLen, size_t InFirstArgCharArrayLen, size_t...InCharArrayLengths>
consteval auto ConstFormatCharArray (const std::array<char, InFormatCharArrayLen> & Fmt, const std::array<char, InFirstArgCharArrayLen> FirstArg, const std::array<char, InCharArrayLengths>...Args) {
    auto NextPlaceHolderPosition = ConstFindNextPlaceHolderPosition(Fmt);
    auto FollowingFormat = ConstSubStr<NextPlaceHolderPosition + 2>(Fmt);
    auto FollowingFormatted = ConstFormatCharArray(FollowingFormat, Args...);
    auto FirstFormat = ConstSubStr<0, NextPlaceHolderPosition>(Fmt);
    return ConstConcatCharArray(ConstConcatCharArray(FirstFormat, FirstArg), FollowingFormatted);
}

consteval uint64_t ConstCStringHash (const char * str, uint64_t hash = 0) {
    while (*str) {
        hash += *str++;
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash;
}

template<size_t InCharArrayLen>
consteval uint64_t ConstCharArrayHash (std::array<char, InCharArrayLen> Arr, uint64_t hash = 0) {
    for (auto &c : Arr) {
        hash += c;
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash;
}

inline consteval uint64_t ConstStringHash (const char * str, uint64_t hash = 0) {
    while (*str) {
        hash += *str++;
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash;
}

inline uint64_t StringHash (const char * str, uint64_t hash = 0) {
    while (*str) {
        hash += *str++;
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash;
}
CB_NAMESPACE_END

#endif //CELESTIALBEING_CONSTEVAL_HPP
